import asyncio
import concurrent
import socket
import threading
import random
import aiohttp
import platform
import subprocess
import sys
import time
from concurrent.futures import ThreadPoolExecutor
from functools import lru_cache
import requests
import  json
import pybase64 as base64
from aiohttp import ClientTimeout

from cls import LocalFile, SubConvert, StrText, IpAddress

# 配置


# 全局变量
unique_node_names = set()
nodecount = 0
datecont = time.strftime('%m-%d', time.localtime(time.time()))
v2ry_nodes = ''
clashurl = ''
openclashurl = ''
clash_node_url = ''
proxies_url = ''
clashname = ''
telename = ''
lock = threading.Lock()
# 创建线程本地存储
thread_local = threading.local()


def get_thread_local_data():
    # 如果线程本地存储中没有 'data'，则初始化它
    if not hasattr(thread_local, 'data'):
        thread_local.data = {}
    return thread_local.data


async def fetch_url(session, url):
    try:
        async with session.get(url, timeout=ClientTimeout(total=250, connect=60)) as response:
            if response.status != 200:
                print(f"[GET Code {response.status}] Download sub error on link: {url}")
                return None
            content = await response.read()
            try:
                print(f"Get node link on sub : {url} ")
                return base64.b64decode(content, encodings="utf-8").decode("utf-8")
            except:
                print('未使用base64加密，直接添加')
                return content.decode('utf-8')
    except Exception as e:
        print(f"[Unknown Error] Download sub error on link: {url}")
        print(e)
        return None


async def select_sub_urls(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        return [result for result in await asyncio.gather(*tasks) if result]


@lru_cache(maxsize=1000)
def get_country(ip):
    return IpAddress.get_country(ip)


def node_handler(node_json):
    global nodecount, clashurl, openclashurl, clash_node_url, proxies_url, clashname, telename, v2ry_nodes

    # 获取线程本地数据

    try:
        # 获取锁
        with lock:
            newname = ""
            onenode = ''
            cipher = ''
            server = ''
            port = ''
            j = node_json.get('link', '')
            if j == '':
                print(f"当前节点不满足条件： {json.dump(node_json)}")
                return

            speed = node_json.get('speed', "") + f'[{random.randint(1, 1000)}'
            if j.startswith("vmess://"):
                j = SubConvert.check_url_v2ray_vmess(j)
                j = base64.b64decode(j[8:]).decode('utf-8')
                node = json.loads(j)
                server = node['add']
                port = node['port']
                newname = f'[{speed}-]' + node['ps']
                cipher = node['scy']
                onenode = SubConvert.v2ray_vmess_to_clash_Speed(j, speed)
                if len(onenode) <10:
                    return
            elif j.startswith("vless://"):
                node = SubConvert.vless_to_clash(j)

                server = node['server']
                port = node['port']

                newname = f'[{datecont}]-[{speed}]-{get_country(server)}-{str(nodecount).zfill(3)}-{server}'
                node['name'] = newname

                onenode = f'  - {json.dump(node)}'

            elif j.startswith("ss://"):
                onenode = SubConvert.url_ss_to_json(j)
                if onenode:
                    node = json.loads(onenode)
                    server = node['server']
                    port = node['port']
                    cipher = node['cipher']
                    password = node['password'].replace('<', '').replace('>', '').replace('!', '')
                    newname = f'[{datecont}]-[{speed}]-{get_country(server)}-{str(nodecount).zfill(3)}-{server}'
                    onenode = f'  - {{name: \'{newname}\', cipher: {cipher}, password: {password}, server: {server}, port: {port}, type: ss}}'
                else:
                    return
            elif j.startswith("trojan://"):
                server = StrText.get_str_btw(j, "@", ":", 0)
                port = StrText.get_str_btw(j, "@", "#", 0).split(":", 1)[1] if j.find("?") == -1 else StrText.get_str_btw(
                    StrText.get_str_btw(j, "@", "#", 0), ":", "?", 0)
                password = StrText.get_str_btw(j, "trojan://", "@", 0).replace('<', '').replace('>', '')
                newname = f'[{datecont}]-[{speed}]-{get_country(server)}-{str(nodecount).zfill(3)}-{server}'
                onenode = f'  - {{name: \'{newname}\', server: {server}, port: {port}, type: trojan, password: {password}'
                if '?' in j:
                    tmpstr = StrText.get_str_btw(j, "?", "#", 0) + '&'
                    if "sni=" in j:
                        onenode += f', sni: {StrText.get_str_btw(tmpstr, "sni=", "&", 0)}'
                    onenode += ', tls: true, skip-cert-verify: true'
                onenode += '}'
            elif j.startswith("ssr://"):
                onenode = base64.b64decode(j[6:]).decode('utf-8')
                node = onenode.split('/?')[0].split(':')
                server = node[0]
                port = node[1]
                protocol = node[2]
                cipher = node[3]
                obfs = node[4]
                password = base64.b64decode(node[5]).decode('utf-8').replace('<', '').replace('>', '')
                newname = f'[{datecont}]-[{speed}]-{get_country(server)}-{str(nodecount).zfill(3)}-{server}'
                onenode = f'  - {{name: \'{newname}\', server: {server}, port: {port}, type: ssr, cipher: {cipher}, password: {password}, protocol: {protocol}, obfs: {obfs}}}'
            else:
                return

            if (server+':' + str(port)) not in unique_node_names:
                if (newname.find(u'省') > -1 or newname.find(u'上海') > -1 or newname.find(
                        u'北京') > -1 or newname.find(u'重庆') > -1 or newname.find(u'广州') > -1 or newname.find(u'内蒙') > -1):
                    return
                unique_node_names.add(server+':' + str(port))
                nodecount += 1
                clashname += f'  - \'{newname}\'\n'
                clashurl += f'{onenode}\n'
                openclashurl += f'{onenode}\n'
                clash_node_url = clash_node_url + '\n' + onenode.replace('  - {', '  - {"').replace('"', '').replace(
                    '\'', '').replace(': ', '": "').replace(', ', '", "').replace('}', '"}')

                if '伊朗' not in newname and '中非' not in newname:
                    telename += f'  - \'{newname}\'\n'
                proxies_url += f'{onenode}\n'
                print(f'添加第{nodecount}个节点,{newname}:{server}')
            else:
                print('重复已经过滤')

    except Exception as ex:
        print(f"Error processing node: {ex}")


def process_nodes(nodes):

    with concurrent.futures.ThreadPoolExecutor(max_workers=44) as executor:
        futures = [executor.submit(node_handler, j) for j in nodes]

        concurrent.futures.as_completed(futures)


def write_clash_file():
    global clashurl, openclashurl, clash_node_url, proxies_url, clashname, telename

    clashname = clashname.rstrip('\n')
    telename = telename.rstrip('\n')
    clashurl = clashurl.rstrip('\n')
    openclashurl = openclashurl.rstrip('\n')
    clash_node_url = clash_node_url.rstrip('\n')
    proxies_url = proxies_url.rstrip('\n')

    if clashname:
        with open("./res/clash-1.txt", "r", encoding='utf-8') as f:
            clash_1 = f.read()
        with open("./res/clash-2.txt", "r", encoding='utf-8') as f:
            clash_2 = f.read()
        with open("./res/clash-3.txt", "r", encoding='utf-8') as f:
            clash_3 = f.read()

        LocalFile.write_LocalFile('./o/proxies.txt', f'proxies:\n{proxies_url}')
        print('ClashNode-Proxies文件成功写入。(纯节点)')

        tmp = clash_1.replace("clash-url.txt", clashurl.replace("%", ''))
        tmp = tmp.replace("clash-name.txt", clashname)
        tmp = tmp.replace("tele-name.txt", telename)
        tmp = tmp.replace("clash-2.txt", clash_2)
        tmp = tmp.replace('\nexternal-ui: \'/usr/share/openclash/dashboard\'', '')
        LocalFile.write_LocalFile('./o/clash.yaml', tmp)
        print('Clash文件成功写入。')

        tmp = clash_1.replace("clash-url.txt", openclashurl.replace("%", ''))
        tmp = tmp.replace("clash-name.txt", clashname)
        tmp = tmp.replace("tele-name.txt", telename)
        tmp = tmp.replace("clash-2.txt", clash_2)
        LocalFile.write_LocalFile('./o/openclash.yaml', tmp)
        print('OpenClash文件成功写入。(添加UDP为True的参数)')

        tmp = clash_3.replace("clash-url.txt", clashurl.replace("%", ""))
        tmp = tmp.replace("clash-name.txt", clashname.replace('  - \'', '      - \''))
        tmp = tmp.replace("tele-name.txt", telename)
        tmp = tmp.replace("clash-2.txt", clash_3)
        tmp = tmp.replace('\nexternal-ui: \'/usr/share/openclash/dashboard\'', '')
        LocalFile.write_LocalFile('./o/clash_mate.yaml', tmp)
        print('clash_mate文件成功写入。')

        LocalFile.write_LocalFile('./o/clashnode.txt', f'proxies:{clash_node_url}')
        print('ClashNode文件成功写入。(纯节点)')





async def main():

    # allnodetxt = LocalFile.read_LocalFile('./o/to_clash_nodes.txt')
    nodes = []
    # nodes = allnodetxt.split('\n')
    try:
        with open('./o/to_clash_nodes_bak.json', 'r', encoding='utf-8') as file:
            nodes = json.load(file)
            if len(nodes) <= 1:
                print("节点获取失败，不再进行")
                return
            process_nodes(nodes)
            write_clash_file()
    except json.JSONDecodeError as e:
        print(f"JSON 解码错误: {e}")
        print(f"错误发生在第 {e.lineno} 行, 列 {e.colno}")




if __name__ == "__main__":
    asyncio.run(main())
